/* oscillators.cu
 - basic ocsillators running over GPU.
 */

#ifdef __CDT_PARSER__
#define __global__
#define __device__
#define __host__
#define __shared__

#define CUDA_KERNEL_DIM(...)

#else
#define CUDA_KERNEL_DIM(...)  <<< __VA_ARGS__ >>>

#endif

#include <cuda.h>
#include <cutil.h>

#include <math_functions.h>

#include <iostream>
#include <math.h>
//#include <time.h>

#include "oscillators.h"
#include "globals.h"

using namespace std;

#define SIN(ph0, freq, gain, offset) \
	(offset + ( gain * sin(ph0 + (phase * freq) ) ) )

#define SAW1(ph0, freq, gain, offset) \
	(gain*((((phase*freq) - (2*M_PI*floor((phase*freq/(2*M_PI)))) ) / ((float)2*M_PI))-0.5))

#define SAW(ph0, freq, gain, offset) \
	(gain* 2*((((phase+ph0)*freq/2/M_PI) - floor((phase+ph0)*freq/2/M_PI))-0.5) )

#define INV( val ) \
	(val * -1 )



struct osc_params {
	// all values range from 0.0 - 1.0
	float ph0;
	float freq;
	float gain;
	float offset;
};

int threadsPerBlock = 256;

__device__ float triangle( float phase, float ph0, float freq, float gain, float offset) {
	float val = SAW((ph0+M_PI), (freq), gain, offset );
	val = val < 0 ? -1*val : val;
	return (2*val-1);
}

__device__ float i_triangle(float phase, float ph0, float freq, float gain, float offset) {
	float val = SAW((ph0+M_PI), (freq), gain, 0);
	val = val < 0 ? -1*val*val : val*val;
	return val;
}

__device__ float saw(float ph0, float phase, int freq, float gain, float offset) {
	float i = (phase*freq) - 2*M_PI*(floor(phase*freq/2/M_PI));
	return i / 2 / M_PI;
}

__global__ void instrument1(float *buf_d, int sidx, float _phase, float ph0,
		float freq, float gain, float offset) {
	int idx = blockIdx.x * blockDim.x + threadIdx.x;
	float phase = (sidx + idx)* _phase;
//	buf_d[idx] = gain * sinf(ph0 + (phase * (f_invscale(freq))));
	buf_d[idx] = SIN(i_triangle(phase, 0, 10, 10, 0),
						  300,
						  1,
						  0);
	// buf_d[idx] = triangle(phase,
	// 							 0, 200, 1, 0);
	//	(0 + ( 1 * sin((0 + ( 10 * sin(ph0 + (phase * 1) ) ) ) + (phase * 400) ) ) 	
}

__global__ void instrument2(float *buf_d, int sidx, float _phase, float ph0,
		float freq, float gain, float offset) {
	int idx = blockIdx.x * blockDim.x + threadIdx.x;
	float phase = (sidx + idx)* _phase;
//	buf_d[idx] = gain * sinf(ph0 + (phase * (f_invscale(freq))));
	buf_d[idx] = SIN( SIN(ph0, 20, 6, 0),
							f_invscale(freq),
							gain,
							0);
	//	(0 + ( 1 * sin((0 + ( 10 * sin(ph0 + (phase * 1) ) ) ) + (phase * 400) ) ) )
}
__global__ void mixer(float *buf_d, float *buf_d_2) {
	int idx = blockIdx.x * blockDim.x + threadIdx.x;
	buf_d[idx] = (buf_d[idx]*0.6) + (buf_d_2[idx]*0.0);
}


void synthesiser_dispatch(float *buf_d, int num_inst, float **buf_d_grid, int N, int sidx) {
	
	float _phase = 2 * M_PI / SAMPLE_RATE;
	unsigned int numBlocks = (N + threadsPerBlock - 1) / threadsPerBlock;
	
	unsigned int timer = 0;
	//	cutilCheckError(cutCreateTimer(&timer));
	//	cutStartTimer(timer);
	instrument1 <<<numBlocks, threadsPerBlock >>>
		(	buf_d, sidx, _phase, 0,
			f_scaled(400), 1, 0
			);

	for(int i=0; i<num_inst; i++) {
	instrument2 <<<numBlocks, threadsPerBlock >>>
		(	buf_d_grid[i], sidx, _phase, 0,
			f_scaled(500), 1, 0
			);
	}
	cudaThreadSynchronize();
	//	cutStopTimer(timer);
	//	kernel_time += cutGetTimerValue(timer);

	
	timer = 0;
	//	cutCreateTimer(&timer);
	//	cutStartTimer(timer);
	
	mixer <<<numBlocks, threadsPerBlock >>>
		( buf_d, buf_d_grid[0] );
	
	cudaThreadSynchronize();
	//	cutStopTimer(timer);
	//	kernel_time += cutGetTimerValue(timer);

}
